﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Itfort
{
	/// <summary>
	/// 转换。
	/// </summary>
	public static class MyConvert
	{
		/// <summary>
		/// 将字典集合转换为指定泛型类型对象。
		/// </summary>
		/// <typeparam name="T">目标类型。</typeparam>
		/// <param name="dict">字典集合。</param>
		/// <param name="prefix">字典集合键中所带的前缀。</param>
		/// <param name="skipProperties">需要跳过的属性。</param>
		/// <returns></returns>
		public static T ToObject<T>(IDictionary<String, String> dict, String prefix, params string[] skipProperties)
			where T : class
		{
			T t = Activator.CreateInstance<T>();
			SetObject(dict, prefix, t, skipProperties);
			return t;
		}

		/// <summary>
		/// 将字符串键值集合转换为指定泛型类型对象。
		/// </summary>
		/// <typeparam name="T">目标类型。</typeparam>
		/// <param name="nvc">字符串键值集合。</param>
		/// <param name="prefix">参数前缀。</param>
		/// <param name="skipProperties"></param>
		/// <returns></returns>
		public static T ToObject<T>(NameValueCollection nvc, String prefix, params string[] skipProperties)
			where T : class
		{
			Dictionary<String, String> dict = new Dictionary<string, string>();
			Int32 count = nvc.Count;
			for (var i = 0; i < count; i++)
			{
				dict.Add(nvc.GetKey(i), nvc[i]);
			}
			return ToObject<T>(dict, prefix, skipProperties);
		}

		/// <summary>
		/// 将字典集合转换为指定泛型类型对象。
		/// </summary>
		/// <typeparam name="T">目标类型。</typeparam>
		/// <param name="dict">字典集合。</param>
		/// <param name="prefix">字典集合键中所带的前缀。</param>
		/// <param name="t">T类型实体。</param>
		/// <param name="skipProperties">需要跳过的属性。</param>
		public static void SetObject<T>(IDictionary<String, String> dict, String prefix, T t, params string[] skipProperties)
			where T : class
		{
			Type tType = t.GetType();
			Dictionary<string, PropertyInfo> propertyInfoDictionary = MyReflection.GetTypePropertyDictionary(tType);
			Dictionary<string, string> skipPropertyDictionary = new Dictionary<string, string>();
			if (skipProperties != null && skipProperties.Length != 0)
			{
				skipPropertyDictionary = skipProperties.ToDictionary(p => prefix + p, p => p);
			}

			foreach (var kvp in propertyInfoDictionary)
			{
				String key = prefix + kvp.Key;
				PropertyInfo propertyInfo = kvp.Value;
				// 跳过不需要设置的属性
				if (skipPropertyDictionary.ContainsKey(key))
				{
					continue;
				}

				// 属性可写的情况下
				if (propertyInfo.CanWrite)
				{
					String value;
					if (dict.TryGetValue(key, out value))
					{
						bool success;
						object objValue = StringConverters.ConvertElement(propertyInfo.PropertyType, value, out success);

						if (success)
						{
							propertyInfo.SetValue(t, objValue, null);
						}
					}
				}
			}
		}

		/// <summary>
		/// 将字符串键值集合转换为指定泛型类型对象
		/// </summary>
		/// <typeparam name="T">目标类型。</typeparam>
		/// <param name="dict">字符串键值集合。</param>
		/// <param name="prefix">字典集合键中所带的前缀。</param>
		/// <param name="t">T类型实体。</param>
		/// <param name="skipProperties">需要跳过的属性。</param>
		public static void SetObject<T>(NameValueCollection nvc, String prefix, T t, params string[] skipProperties)
			where T : class
		{
			Dictionary<String, String> dict = new Dictionary<string, string>();
			Int32 count = nvc.Count;
			for (var i = 0; i < count; i++)
			{
				dict.Add(nvc.GetKey(i), nvc[i]);
			}

			SetObject(dict, prefix, t, skipProperties);
		}
	}

	internal delegate object StringConverter(string value, out bool success);

	internal static class StringConverters
	{
		static Dictionary<Type, StringConverter> mConverters;
		static Dictionary<Type, StringConverter> Converters
		{
			get
			{
				if (mConverters == null)
				{
					mConverters = new Dictionary<Type, StringConverter>();
					// Primitive
					mConverters.Add(typeof(String), ToString);
					mConverters.Add(typeof(Boolean), ToBoolean);
					mConverters.Add(typeof(Char), ToChar);
					mConverters.Add(typeof(DateTime), ToDateTime);
					mConverters.Add(typeof(Guid), ToGuid);
					mConverters.Add(typeof(SByte), ToSByte);
					mConverters.Add(typeof(Byte), ToByte);
					mConverters.Add(typeof(Int16), ToInt16);
					mConverters.Add(typeof(UInt16), ToUInt16);
					mConverters.Add(typeof(Int32), ToInt32);
					mConverters.Add(typeof(UInt32), ToUInt32);
					mConverters.Add(typeof(Int64), ToInt64);
					mConverters.Add(typeof(UInt64), ToUInt64);
					mConverters.Add(typeof(Single), ToSingle);
					mConverters.Add(typeof(Double), ToDouble);
					mConverters.Add(typeof(Decimal), ToDecimal);
					// Nullable
					mConverters.Add(typeof(Boolean?), ToNullableBoolean);
					mConverters.Add(typeof(Char?), ToNullableChar);
					mConverters.Add(typeof(DateTime?), ToNullableDateTime);
					mConverters.Add(typeof(Guid?), ToNullableGuid);
					mConverters.Add(typeof(SByte?), ToNullableSByte);
					mConverters.Add(typeof(Byte?), ToNullableByte);
					mConverters.Add(typeof(Int16?), ToNullableInt16);
					mConverters.Add(typeof(UInt16?), ToNullableUInt16);
					mConverters.Add(typeof(Int32?), ToNullableInt32);
					mConverters.Add(typeof(UInt32?), ToNullableUInt32);
					mConverters.Add(typeof(Int64?), ToNullableInt64);
					mConverters.Add(typeof(UInt64?), ToNullableUInt64);
					mConverters.Add(typeof(Single?), ToNullableSingle);
					mConverters.Add(typeof(Double?), ToNullableDouble);
					mConverters.Add(typeof(Decimal?), ToNullableDecimal);
					// Array
					mConverters.Add(typeof(String[]), ToStringArray);
					mConverters.Add(typeof(Boolean[]), ToBooleanArray);
					mConverters.Add(typeof(Char[]), ToCharArray);
					mConverters.Add(typeof(DateTime[]), ToDateTimeArray);
					mConverters.Add(typeof(Guid[]), ToGuidArray);
					mConverters.Add(typeof(SByte[]), ToSByteArray);
					mConverters.Add(typeof(Byte[]), ToByteArray);
					mConverters.Add(typeof(Int16[]), ToInt16Array);
					mConverters.Add(typeof(UInt16[]), ToUInt16Array);
					mConverters.Add(typeof(Int32[]), ToInt32Array);
					mConverters.Add(typeof(UInt32[]), ToUInt32Array);
					mConverters.Add(typeof(Int64[]), ToInt64Array);
					mConverters.Add(typeof(UInt64[]), ToUInt64Array);
					mConverters.Add(typeof(Single[]), ToSingleArray);
					mConverters.Add(typeof(Double[]), ToDoubleArray);
					mConverters.Add(typeof(Decimal[]), ToDecimalArray);
				}
				return mConverters;
			}
		}

		public static object ToString(string value, out bool success)
		{
			success = true;
			return value;
		}

		public static object ToBoolean(string value, out bool success)
		{
			bool result;
			success = Boolean.TryParse(value, out result);
			return result;
		}

		public static object ToChar(string value, out bool success)
		{
			char result;
			success = Char.TryParse(value, out result);
			return result;
		}

		public static object ToDateTime(string value, out bool success)
		{
			DateTime result;
			success = DateTime.TryParse(value, out result);
			return result;
		}

		public static object ToGuid(string value, out bool success)
		{
			Guid result;
			try
			{
				result = new Guid(value);
				success = true;
			}
			catch
			{
				result = Guid.Empty;
				success = false;
			}
			return result;
		}

		public static object ToSByte(string value, out bool success)
		{
			sbyte result;
			success = SByte.TryParse(value, out result);
			return result;
		}

		public static object ToByte(string value, out bool success)
		{
			byte result;
			success = Byte.TryParse(value, out result);
			return result;
		}

		public static object ToInt16(string value, out bool success)
		{
			short result;
			success = Int16.TryParse(value, out result);
			return result;
		}

		public static object ToUInt16(string value, out bool success)
		{
			ushort result;
			success = UInt16.TryParse(value, out result);
			return result;
		}

		public static object ToInt32(string value, out bool success)
		{
			int result;
			success = Int32.TryParse(value, out result);
			return result;
		}

		public static object ToUInt32(string value, out bool success)
		{
			uint result;
			success = UInt32.TryParse(value, out result);
			return result;
		}

		public static object ToInt64(string value, out bool success)
		{
			long result;
			success = Int64.TryParse(value, out result);
			return result;
		}

		public static object ToUInt64(string value, out bool success)
		{
			ulong result;
			success = UInt64.TryParse(value, out result);
			return result;
		}

		public static object ToSingle(string value, out bool success)
		{
			float result;
			success = Single.TryParse(value, out result);
			return result;
		}

		public static object ToDouble(string value, out bool success)
		{
			double result;
			success = Double.TryParse(value, out result);
			return result;
		}

		public static object ToDecimal(string value, out bool success)
		{
			decimal result;
			success = Decimal.TryParse(value, out result);
			return result;
		}

		public static object ToNullableBoolean(string value, out bool success)
		{
			bool? result = (bool)ToBoolean(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableChar(string value, out bool success)
		{
			char? result = (char)ToChar(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableDateTime(string value, out bool success)
		{
			DateTime? result = (DateTime)ToDateTime(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableGuid(string value, out bool success)
		{
			Guid? result = (Guid)ToGuid(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableSByte(string value, out bool success)
		{
			sbyte? result = (sbyte)ToSByte(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableByte(string value, out bool success)
		{
			byte? result = (byte)ToByte(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableInt16(string value, out bool success)
		{
			short? result = (short)ToInt16(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableUInt16(string value, out bool success)
		{
			ushort? result = (ushort)ToUInt16(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableInt32(string value, out bool success)
		{
			int? result = (int)ToInt32(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableUInt32(string value, out bool success)
		{
			uint? result = (uint)ToInt32(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableInt64(string value, out bool success)
		{
			long? result = (long)ToInt64(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableUInt64(string value, out bool success)
		{
			ulong? result = (ulong)ToInt64(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableSingle(string value, out bool success)
		{
			float? result = (float)ToSingle(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableDouble(string value, out bool success)
		{
			double? result = (double)ToDouble(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ToNullableDecimal(string value, out bool success)
		{
			decimal? result = (decimal)ToDecimal(value, out success);
			if (!success)
			{
				result = null;
			}
			success = true;
			return result;
		}

		public static object ConvertElement(Type elementType, string value, out bool success)
		{
			StringConverter converter;
			if (Converters.TryGetValue(elementType, out converter))
			{
				return converter(value, out success);
			}
			else
			{
				success = false;
				return null;
			}
		}

		public static object ToArray(Type elementType, string value, out bool success)
		{
			success = (value != null) && (value != string.Empty);
			if (!success)
			{
				return null;
			}

			string[] strArray = value.Split(new char[] { ',' });
			Array array = Array.CreateInstance(elementType, strArray.Length);
			for (int i = 0; i < strArray.Length; i++)
			{
				object elementResult = ConvertElement(elementType, value, out success);
				if (!success)
				{
					return null;
				}
				array.SetValue(elementResult, i);
			}
			return array;
		}

		public static object ToStringArray(string value, out bool success)
		{
			return ToArray(typeof(String), value, out success);
		}

		public static object ToBooleanArray(string value, out bool success)
		{
			return ToArray(typeof(Boolean), value, out success);
		}

		public static object ToCharArray(string value, out bool success)
		{
			return ToArray(typeof(Char), value, out success);
		}

		public static object ToSByteArray(string value, out bool success)
		{
			return ToArray(typeof(SByte), value, out success);
		}

		public static object ToByteArray(string value, out bool success)
		{
			return ToArray(typeof(Byte), value, out success);
		}

		public static object ToInt16Array(string value, out bool success)
		{
			return ToArray(typeof(Int16), value, out success);
		}

		public static object ToUInt16Array(string value, out bool success)
		{
			return ToArray(typeof(UInt16), value, out success);
		}

		public static object ToInt32Array(string value, out bool success)
		{
			return ToArray(typeof(Int32), value, out success);
		}

		public static object ToUInt32Array(string value, out bool success)
		{
			return ToArray(typeof(UInt32), value, out success);
		}

		public static object ToInt64Array(string value, out bool success)
		{
			return ToArray(typeof(Int64), value, out success);
		}

		public static object ToUInt64Array(string value, out bool success)
		{
			return ToArray(typeof(UInt64), value, out success);
		}

		public static object ToSingleArray(string value, out bool success)
		{
			return ToArray(typeof(Single), value, out success);
		}

		public static object ToDoubleArray(string value, out bool success)
		{
			return ToArray(typeof(Double), value, out success);
		}

		public static object ToDecimalArray(string value, out bool success)
		{
			return ToArray(typeof(Decimal), value, out success);
		}

		public static object ToDateTimeArray(string value, out bool success)
		{
			return ToArray(typeof(DateTime), value, out success);
		}

		public static object ToGuidArray(string value, out bool success)
		{
			return ToArray(typeof(Guid), value, out success);
		}
	}
}
